package ru.ipo.dces.client.components;

import info.clearthought.layout.TableLayout;
import org.jdesktop.swingx.autocomplete.AutoCompleteDecorator;
import ru.ipo.dces.client.Controller;
import ru.ipo.dces.clientservercommunication.AvailableContestsRequest;
import ru.ipo.dces.clientservercommunication.AvailableContestsResponse;
import ru.ipo.dces.clientservercommunication.ContestDescription;
import ru.ipo.dces.plugins.admin.components.ContestList;
import ru.ipo.dces.plugins.admin.beans.ContestsListBean;
import ru.ipo.dces.server.ServerFailReason;
import ru.ipo.dces.utils.ResponseHandler;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.HashSet;
import java.util.*;

/**
 * Created by IntelliJ IDEA.
 * User: Посетитель
 * Date: 11.04.2009
 * Time: 18:59:58
 */
public class ContestChoosingPanel extends JPanel {

    public static final Color CONTEST_HAS_NOT_BEGAN = Color.GREEN;
    public static final Color CONTEST_FINISHED = Color.RED;
    public static final Color CONTEST_IS_NOW = Color.ORANGE;

    private ContestDescription contest = null;
    private boolean popup = false;
    private boolean showAdminContest = false;
    private final ContestList selectContestList;
    private boolean showLabel = true;
    private int beforeLabelGap = 20;
    private final JLabel selectedContestLabel;
    private final JButton selectContestButton;
    private final JButton refreshContestListButton;
    private final JLabel titleLabel;
    private final JTextField searchContestField;

    //TODO implement listeners by встроенными средствами
    private HashSet<ActionListener> contestChangedListeners = new HashSet<ActionListener>();

    private ChooseContestDialog chooseContestDialog = new ChooseContestDialog();

    private boolean noContestsAvailable = true;

    public void selectSelectContestButtonEnable(boolean enable) {
        selectContestButton.setEnabled(enable);
    }

    public ContestChoosingPanel() {
        selectedContestLabel = new JLabel();
        selectedContestLabel.setFont(selectedContestLabel.getFont().deriveFont(Font.BOLD));
        selectContestButton = new JButton("Выбрать");
        refreshContestListButton = new JButton("Обновить");
        selectContestList = new ContestList();
        titleLabel = new JLabel("Доступные соревнования");
        searchContestField = new JTextField();

        selectContestButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                execute();
            }
        });

        refreshContestListButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                refreshContestList();
                setContest(null);
            }
        });

        selectContestList.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent e) {
                if (noContestsAvailable) return;

                ContestsListBean bean = (ContestsListBean) selectContestList.getSelectedValue();
                if (bean == null) return;
                setContest(bean.getDescription());

                //searchContestField.setText(bean.toString());
            }
        });

        searchContestField.getDocument().addDocumentListener(new DocumentListener() {
            public void insertUpdate(DocumentEvent e) {
                doSearchContest();
            }

            public void removeUpdate(DocumentEvent e) {
                doSearchContest();
            }

            public void changedUpdate(DocumentEvent e) {
                //do nothing
            }
        });

        setInterface();

    }

    public void execute() {
        if (isPopup())
            chooseContestDialog.run(this);
    }

    private void doSearchContest() {
        final String inp = searchContestField.getText();

        final ListModel listModel = selectContestList.getModel();
        final int size = listModel.getSize();
        for (int i = 0; i < size; i++) {
            final String name = listModel.getElementAt(i).toString();
            if (name.startsWith(inp)) {
                //select element in list
                selectContestList.setSelectedIndex(i);
                selectContestList.ensureIndexIsVisible(i);

                break;
            }
        }

        selectContestList.setSelectedIndex(-1);
    }

    public void refreshContestList() {
        refreshContestListButton.setEnabled(false);
        ResponseHandler<AvailableContestsResponse> getDisplayableContestListHandler =
                new ResponseHandler<AvailableContestsResponse>() {
                    @Override
                    public void success(AvailableContestsResponse response) {
                        ContestDescription[] contests = response.contests;

                        Comparator<ContestDescription> descriptionComparator = new Comparator<ContestDescription>() {
                            public int compare(ContestDescription contest1, ContestDescription contest2) {

                                int t1 = contestTimingType(contest1);
                                int t2 = contestTimingType(contest2);

                                return t1 - t2;
                            }
                        };

                        Arrays.sort(contests, descriptionComparator);
                        if (isShowAdminContest()) {
                            contests = Arrays.copyOf(contests, contests.length + 1);
                            contests[contests.length - 1] = createAdminContest();
                        }

                        updateContestsTable(contests);
                        refreshContestListButton.setEnabled(true);
                    }

                    @Override
                    public void fail(ServerFailReason exception) {
                        refreshContestListButton.setEnabled(true);
                    }
                };
        getContestList(getDisplayableContestListHandler);
    }

    private void updateContestsTable(ContestDescription desc[]) {
        ContestsListBean[] contestDescriptions = new ContestsListBean[desc.length];
        for (int i = 0; i < desc.length; i++)
            contestDescriptions[i] = new ContestsListBean(desc[i]);

        noContestsAvailable = (contestDescriptions.length == 0);
        ListModel listModel;

        if (noContestsAvailable) {
            listModel = new AbstractListModel() {
                public int getSize() {
                    return 1;
                }

                public Object getElementAt(int index) {
                    return "Нет доступных соревнований";
                }
            };
            AutoCompleteDecorator.decorate(searchContestField, Arrays.asList(), true);

        } else {
            listModel = new DefaultComboBoxModel(contestDescriptions);
            AutoCompleteDecorator.decorate(searchContestField, Arrays.asList(contestDescriptions), true);
        }

        selectContestList.setDescription(desc);
        selectContestList.setEnabled(!noContestsAvailable);
//        selectContestList.setModel(listModel);
        selectContestList.setSelectedIndex(-1);
    }

    //TODO remove in all project
    /*public void refreshContestList() {
        ContestsListBean[] contestDescriptions = getDisplayableContestList();

        noContestsAvailable = (contestDescriptions.length == 0);

        ListModel listModel;

        if (noContestsAvailable) {
            listModel = new AbstractListModel() {
                public int getSize() {
                    return 1;
                }

                public Object getElementAt(int index) {
                    return "Нет доступных соревнований";
                }
            };
            AutoCompleteDecorator.decorate(searchContestField, Arrays.asList(), true);

        } else {
            listModel = new DefaultComboBoxModel(contestDescriptions);
            AutoCompleteDecorator.decorate(searchContestField, Arrays.asList(contestDescriptions), true);
        }


        class IconListRenderer extends DefaultCellRenderer {

            @Override
            public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
                                                          boolean cellHasFocus) {

                JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected,
                        cellHasFocus);

                Icon icon = null;
                ContestsListBean bean = (ContestsListBean) value;

                if (bean.getDescription().registrationType != null) {
                    switch (bean.getDescription().registrationType) {
                        case ByAdmins:
                            icon = ImageResources.ADMIN_REGISTRATION_IMAGE;
                            break;
                        case Self:
                            icon = ImageResources.SELF_REGISTRATION_IMAGE;
                            break;
                        default:
                    }

                    Date currentTime = new Date();
                    if (bean.getDescription().finish.compareTo(currentTime) < 0)
                        label.setForeground(CONTEST_FINISHED);
                    else if (bean.getDescription().start.compareTo(currentTime) > 0)
                        label.setForeground(CONTEST_HAS_NOT_BEGAN);
                    else
                        label.setForeground(CONTEST_IS_NOW);
                }

                if (bean.getDescription().contestID == 0)
                    label.setForeground(Color.GRAY);

                if (icon != null)
                    label.setIcon(icon);
                return label;
            }
        }

        selectContestList.setEnabled(!noContestsAvailable);
        selectContestList.setCellRenderer(new IconListRenderer());
        selectContestList.setModel(listModel);
        selectContestList.setSelectedIndex(-1);
    }*/

    private void setInterface() {
        removeAll();

        if (popup) {

            if (showLabel) {
                setLayout(new TableLayout(new double[][]{
                        {beforeLabelGap, TableLayout.PREFERRED, TableLayout.FILL, TableLayout.PREFERRED}, {TableLayout.FILL}
                }));

                add(new JLabel("Соревнование "), "1, 0");
                add(selectedContestLabel, "2, 0");
                add(selectContestButton, "3, 0");
            } else {
                setLayout(new GridLayout(1, 1));
                add(selectContestButton);
            }

        } else {
            setLayout(new TableLayout(new double[][]{
                    {TableLayout.FILL, TableLayout.PREFERRED}, {TableLayout.PREFERRED, TableLayout.FILL, TableLayout.PREFERRED}
            }));

            add(titleLabel, "0, 0");
            add(refreshContestListButton, "1, 0");
            add(new JScrollPane(selectContestList), "0, 1, 1, 1");
            add(searchContestField, "0, 2, 1, 2");
        }

        validate();
    }

    public boolean isPopup() {
        return popup;
    }

    public void setPopup(boolean popup) {
        this.popup = popup;
        setInterface();
    }

    public boolean isShowLabel() {
        return showLabel;
    }

    public void setShowLabel(boolean showLabel) {
        this.showLabel = showLabel;
        setInterface();
    }

    public ContestDescription getContest() {
        return contest;
    }

    //TODO move selection in list if the list is visible

    public void setContest(ContestDescription contest) {
        this.contest = contest;
        String contestDisplayName = contest == null ? "" : contest.name;
        selectedContestLabel.setText(contestDisplayName);

        for (ActionListener contestChangedListener : contestChangedListeners)
            contestChangedListener.actionPerformed(new ActionEvent(this, 0, null));
    }

    public int getBeforeLabelGap() {
        return beforeLabelGap;
    }

    public void setBeforeLabelGap(int beforeLabelGap) {
        this.beforeLabelGap = beforeLabelGap;
        setInterface();
    }

    public String getTitle() {
        return titleLabel.getText();
    }

    public void setTitle(String title) {
        titleLabel.setText(title);
    }

    public boolean isShowAdminContest() {
        return showAdminContest;
    }

    public void setShowAdminContest(boolean showAdminContest) {
        this.showAdminContest = showAdminContest;
        refreshContestList();
    }

    //action listeners

    public void addContestChangedActionListener(ActionListener l) {
        contestChangedListeners.add(l);
    }

    public void removeContestChangedActionListener(ActionListener l) {
        contestChangedListeners.remove(l);
    }

    public static void getContestList(final ResponseHandler<AvailableContestsResponse> handler) {
        Controller.doRequest(new AvailableContestsRequest(), handler, "Получение списка доступных соревнований");
    }

    /*public static ContestsListBean[] getContestList() {
        ContestDescription[] contestDescriptions = Controller.getAvailableContests();
        ContestsListBean[] result = new ContestsListBean[contestDescriptions.length];

        for (int i = 0; i < contestDescriptions.length; i++)
            result[i] = new ContestsListBean(contestDescriptions[i]);

        return result;
    }*/

    //TODO remove in all project

    /**
     * Return contests list intended to be displayed
     *
     * @return contests list
     */
    /*private ContestsListBean[] getDisplayableContestList() {
        ContestsListBean[] result = getContestList();

        Comparator<ContestsListBean> beanComparator = new Comparator<ContestsListBean>() {
            public int compare(ContestsListBean bean1, ContestsListBean bean2) {

                int t1 = contestTimingType(bean1.getDescription());
                int t2 = contestTimingType(bean2.getDescription());

                return t1 - t2;
            }
        };

        Arrays.sort(result, beanComparator);

        if (isShowAdminContest()) {
            result = Arrays.copyOf(result, result.length + 1);
            result[result.length - 1] = new ContestsListBean(createAdminContest());
        }

        return result;
    }*/

    /**
     * Returns contest timing type: 2 - passed, 0 - current, 1 - future. If start or finish time is null
     * then timing type is 1
     *
     * @param c contest description to test
     * @return timing type
     */
    private int contestTimingType(ContestDescription c) {
        if (c.start == null || c.finish == null)
            return 1;
        Date now = new Date();
        if (now.compareTo(c.start) < 0)
            return 1;
        if (now.compareTo(c.finish) > 0)
            return 2;
        return 0;
    }

    private static ContestDescription createAdminContest() {
        ContestDescription adminContest = new ContestDescription();
        adminContest.contestID = 0;
        adminContest.name = "Администрирование";
        adminContest.description = "Администрирование сервера соревнований";
        return adminContest;
    }
}